import { useState, useRef, useEffect } from "react";
import { ProTable } from "@ant-design/pro-components";
import API from "./service";
import {
  Button, Form, Drawer, Space, Input, Select, Row, Col, Descriptions, message,
  Upload, Image, Avatar, Popover,
} from "antd";
import { PlusOutlined, FileImageOutlined } from "@ant-design/icons";
import OperationBar from "../../components/OperationBar";
import { TYPE } from "../types";
import type { ProColumns } from "@ant-design/pro-components";
import type { DescriptionsItemType } from "antd/es/descriptions";
import type { DefaultOptionType } from "antd/es/select";
import type { UploadFile } from "antd";
import { RcFile } from "antd/es/upload";
import { useAccess, Access } from "@umijs/max";
import { isLight, apiUrl } from "../../../config/defaultSettings";
import styles from "./index.css";

const required = true;
const titleMap = { Add: "新增", Edit: "编辑" };
const keys = "name,useFor,price,tag,color,brand,height,width,sequ,remark,images";
const labels = "名称,适用于，用途,单价,标签,颜色,品牌,高度,直径,显示顺序,备注,图集";
const actionStr = "新瓶入库,上架,售出,下架返库,丢弃";
const actionMap: Record<number, string> = {};
const actionData: DefaultOptionType[] = actionStr.split(",").map((label, i) => {
  actionMap[i] = label;
  return { label, value: i };
});
const useForStr = "名酒酒瓶,家具摆件,小花盆,笔筒,茶叶罐,烟灰缸";
const useForMap: Record<number, string> = {};
const useForData: DefaultOptionType[] = useForStr.split(",").map((label, i) => {
  useForMap[i] = label;
  return { label, value: i };
});
let startTime = 0;

/**
 * 限制文件上传数量
 */
const uploadMaxCount = 7;

/**
 * 缩略图尺寸
 */
const thumbSize = 360;

/**
 * 限制单个文件大小
 */
const limitSize = 1024 * 1024 * 1;

export default () => {
  const [visible, visibleSet] = useState(false);
  const [saveLoading, saveLoadingSet] = useState(false);
  const [action, actionSet] = useState<"Add" | "Edit">("Add");
  const [editId, editIdSet] = useState(0);
  const [display, displaySet] = useState<"form" | "desc">("form");
  const [descItems, descItemSet] = useState<DescriptionsItemType[]>([]);
  const [formValues, formValueSet] = useState<any>({});
  const [previewVisible, previewVisibleSet] = useState(false);
  const [previewImage, previewImageSet] = useState("");
  const [fileList, fileListSet] = useState<UploadFile[]>([]);
  const [help, helpSet] = useState("");

  const [formRef] = Form.useForm();
  const tableRef = useRef<any>({});
  const ac = useAccess();

  useEffect(() => {
    // 文件上传列表联动表单值
    const imagesData = fileList.filter(item => item.status === "done")
      .map(item => item.name);
    const images = imagesData.toString();
    formRef.setFieldValue("images", images);
    formValueSet({ ...formValues, images });
    if (!fileList.length) helpSet("请上传图集，最多" + uploadMaxCount + "张。");
    else if (imagesData.length === fileList.length && startTime) {
      const now = new Date();
      let diff = Math.round((now.getTime() - startTime) / 1000);
      let unit = "秒";
      if (diff >= 60) {
        diff = parseFloat((diff / 60).toFixed(1));
        unit = "分钟";
      }
      startTime = 0;
      helpSet("上传完成，用时：" + diff + unit);
    }

    // 上传流程控制
    let isUploading = false; // 逐个上传，而不是扎堆上传。
    fileList.forEach(({ percent }) => {
      const p = percent || 0;
      if (p > 65 && p < 100) isUploading = true;
    });
    fileList.forEach(fileItem => {
      const { status, percent, originFileObj } = fileItem;
      const file = originFileObj as RcFile;
      const p = percent || 0;
      if (status !== "uploading") return;
      if (p === 0) {
        if (file.type === "image/jpeg") handleImage(file);
        else uploadVideo(file);
        if (!startTime) {
          const now = new Date();
          const time = now.toTimeString().substring(0, 8);
          startTime = now.getTime();
          helpSet("开始上传 " + time);
        }
      } else if (p >= 10 && p < 65) {
        if (file.type === "image/jpeg") adjustImage(fileItem);
      } else if (p === 65) {
        if (!isUploading) {
          upload(fileItem);
          isUploading = true;
        }
      }
    });
  }, [fileList]);

  const columns: ProColumns[] = [
{
       title: "图集",
       dataIndex: "images",
       hideInSearch: true,
       width: 200,
       renderText: (text: string) => (
        <Avatar.Group shape="square">
          {text.split(",").map(p => p.split(".")).map(([fileName, ext], i) => {
            const name = apiUrl + "file/" + fileName;
            const src = name + "_thumb." + ext;
            return (
              <Popover key={i} placement="left" content={<img src={src} />}>
                <Avatar icon={<FileImageOutlined />} shape="square" src={src} />
              </Popover>
            );
          })}
        </Avatar.Group>
       ),
     },
     {
      title: "名称",
      dataIndex: "name",
      renderText: (text, record: TYPE.BottleItem) => (
        <OperationBar
          text={text}
          display="hover"
          textOnClick={() => {
            updateItems(record);
            openDrawer("desc");
          }}
          editOnClick={() => {
            formRef.setFieldsValue(record);
            actionSet("Edit");
            editIdSet(record.id);
            initFileList(record.images);
            openDrawer("form");
          }}
          deleteOnClick={() => remove(record.id)}
        />
      ),
    },
    { title: "适用于", dataIndex: "useFor", width: 90, valueEnum: useForMap },
    {
      title: "单价",
      dataIndex: "price",
      renderText: text => text + "元",
      hideInSearch: true,
      width: 60,
    },
    {
      title: "库存",
      dataIndex: "num0",
      hideInSearch: true,
      width: 120,
      renderText: (num: number, record: TYPE.BottleItem) => (
        <div className={styles.numBar}>
          <div>{num}</div>
          <Access accessible={ac.canUse("stockIn")}>
            <a
              onClick={() => {
                const value = prompt("请输入入库数量：", "1");
                if (value) API.stockIn(record.id, parseInt(value))
                  .then(() => tableRef.current.reload());
              }}
            >入库</a>
          </Access>
          <Access accessible={num > 0 && ac.canUse("takeUp")}>
            <a
              style={{ color: "green" }}
              onClick={() => {
                const value = prompt("请输入上架数量：", "1");
                if (value) API.takeUp(record.id, parseInt(value))
                  .then(() => tableRef.current.reload());
              }}
            >上架</a>
          </Access>
          <Access accessible={num > 0 && ac.canUse("abandon")}>
            <a
              style={{ color: "red" }}
              onClick={() => {
                const value = prompt("请输入丢弃数量：", "1");
                if (value) API.abandon(record.id, parseInt(value))
                  .then(() => tableRef.current.reload());
              }}
            >丢弃</a>
          </Access>
        </div>
      ),
    },
    {
      title: "销售中",
      dataIndex: "num1",
      hideInSearch: true,
      width: 80,
      renderText: (num: number, record: TYPE.BottleItem) => (
        <div className={styles.numBar}>
          <div>{num}</div>
          {num > 0 && (
            <>
              <Access accessible={ac.canUse("dispose")}>
                <a
                  style={{ color: "green" }}
                  onClick={() => {
                    const value = prompt("请输入售出数量：", "1");
                    if (value) API.dispose(record.id, parseInt(value))
                      .then(() => tableRef.current.reload());
                  }}
                >售出</a>
              </Access>
              <Access accessible={ac.canUse("takeDown")}>
                <a
                  style={{ color: "red" }}
                  onClick={() => {
                    const value = prompt("请输入下架数量：", "1");
                    if (value) API.takeDown(record.id, parseInt(value))
                      .then(() => tableRef.current.reload());
                  }}
                >下架</a>
              </Access>
            </>
          )}
        </div>
      ),
    },
    {
      title: "已售出",
      dataIndex: "num2",
      hideInSearch: true,
      width: 80,
      renderText: (num: number) => (
        <div className={styles.numBar}>
          <div>{num}</div>
        </div>
      ),
    },
    {
      title: "发布日期",
      dataIndex: "addTime",
      renderText: text => text ? text.substr(0, 10) : null,
      hideInSearch: true,
      width: 110,
    },
    { title: "标签", dataIndex: "tag", hideInTable: true },
    { title: "颜色", dataIndex: "color", hideInTable: true },
    { title: "品牌", dataIndex: "brand", hideInTable: true },
  ];

  const save = () => {
    formRef.validateFields().then((formData: TYPE.BottleItem) => {
      if (action === "Edit") {
        formData = { ...formValues };
        if (JSON.stringify(formData) === "{}") {
          message.info("你未做任何更改");
          return;
        }

        formData.id = editId;
      } else {
        for (const key in formData) {
          const value = formData[key];
          if (value === undefined) delete formData[key];
        }
      }
      // 字符串转数字(隐式转换)
      if (formData.price) formData.price -= 0;
      if (formData.height) formData.height -= 0;
      if (formData.width) formData.width -= 0;
      saveLoadingSet(true);
      const func = action === "Add" ? API.bottleAdd : API.bottleEdit;
      func(formData).then(() => {
        saveLoadingSet(false);
        visibleSet(false);
        tableRef.current.reload();
      }).catch(() => saveLoadingSet(false));
    });
  };

  const remove = (id: number) => {
    API.bottleRemove(id).then(() => tableRef.current.reload());
  };

  const removeFile = (filePath: string) => {
    API.bottleRemoveFile(filePath);
  };

  const updateItems = (record: any) => {
    const { id } = record;
    const keyData = keys.split(",");
    const items: DescriptionsItemType[] = labels.split(",").map((label, i) => ({
      key: keyData[i], label, children: "", span: 1,
    }));
    for (const key in record) {
      let value = record[key];
      // 渲染值
      switch (key) {
        case "useFor":
          value = useForMap[value];
          break;

        case "price":
          value += "元";
          break;

        case "height":
        case "width":
          value = (value / 10).toString() + "厘米";
          break;

        case "addTime":
          value = value.substr(0, 10);
          break;

        case "tag":
        case "remark":
          if (!value) value = "无";
          break;

        case "images":
          if (!value) {
            value = "无";
            break;
          }

          const data = (value as string).split(",")
            .map(filePath => filePath.split("."));
          value = (
            <div className={styles.descImages}>
              {data.map(([name, ext], index) => {
                const url = apiUrl + "file/" + name;
                return (
                  <img
                    key={index}
                    alt={name + "." + ext}
                    title={name + "." + ext}
                    src={url + "_thumb." + ext}
                    onClick={() => {
                      previewImageSet(url + "." + ext);
                      previewVisibleSet(true);
                    }}
                    onLoad={e => {
                      // 预加载大图，在点缩略图时就能展示大图。
                      const img = e.target as HTMLImageElement;
                      const img0 = document.createElement("img");
                      img0.src = apiUrl + "file/" + img.alt;
                      console.log("get", img0.src);
                      img0.onload = f => {
                        const myImg = f.target as HTMLImageElement;
                        console.log("onload", myImg.src);
                      };
                    }}
                  />
                );
              })}
            </div>
          );
          break;
      }
      const index = keyData.indexOf(key);
      if (items[index]) items[index].children = value;
      // span=filled 表示独占一行
      if (key === "remark" || key === "images") items[index].span = "filled";
    }
    descItemSet(items);
    editIdSet(id);
  };

  const openDrawer = (displayMode: "form" | "desc") => {
    displaySet(displayMode);
    visibleSet(true);
    if (displayMode === "form") formValueSet({});
  };

  const initFileList = (images: string) => {
    if (!images) {
      fileListSet([]);
      return;
    }

    const data: UploadFile[] = images.split(",").map(filePath => {
      const [fileName, ext] = filePath.split(".");
      const name = apiUrl + "file/" + fileName;
      const fileItem: UploadFile = {
        uid: filePath,
        name: filePath,
        status: "done",
        url: name + "." + ext,
        thumbUrl: name + "_thumb." + ext,
      };
      return fileItem;
    });
    fileListSet(data);
  };

  const uploadOnChange = (info: any) => {
    const files = info.fileList as UploadFile[];
    const newList: UploadFile[] = [];
    for (let i = 0; i < files.length; i += 1) {
      const fileItem = files[i];
      const {
        uid, url, thumbUrl, name, percent, status, originFileObj, linkProps,
      } = fileItem;
      const type = originFileObj?.type;
      const isTypeError = type !== "image/jpeg" && type !== "video/mp4";
      const newItem: UploadFile = {
        uid,
        url,
        thumbUrl,
        name,
        percent,
        status: status || (isTypeError ? "error" : "uploading"),
        originFileObj,
      };
      if (type === "image/jpeg") {
        newItem.linkProps = linkProps || {
          b64: "", quality: 1, width: 0, height: 0,
        };
      }
      newList.push(newItem);
    }
    fileListSet(newList);
  };

  const handleImage = (file: RcFile) => {
    let fileItem: UploadFile = { uid: "", name: "" };
    for (let i = 0; i < fileList.length; i += 1) {
      fileItem = fileList[i];
      if (fileItem.name === file.name) break;
    }

    const reader = new FileReader();
    reader.onload = () => {
      // 获取图片的base64编码
      const b64 = reader.result as string;
      const img = document.createElement("img");
      img.style.visibility = "hidden";
      img.style.position = "absolute";
      img.style.top = "0px";
      // 把图片加入页面是为了使用图片的尺寸特性
      // 例如改变图片的高度，则图片的宽度会等比例自动变化。
      document.body.append(img);
      img.onload = () => {
        const { width, height } = img;
        fileItem.percent = 10;
        fileItem.url = b64;
        fileItem.linkProps.b64 = b64;
        fileItem.linkProps.width = width;
        fileItem.linkProps.height = height;
        if (width <= thumbSize && height <= thumbSize) {
          // 小图直接使用，无须缩小了。
          fileItem.thumbUrl = b64;
          fileItem.preview = b64;
          fileListSet([...fileList]);
          document.body.removeChild(img);
          return;
        }

        // 生成缩略图
        const canvas = document.createElement("canvas");
        canvas.width = thumbSize;
        canvas.height = thumbSize;
        let x = 0, y = 0;
        if (width <= height) {
          // 竖图
          img.height = thumbSize;
          x = (thumbSize - img.width) / 2;
        } else {
          // 横图、宽图
          img.width = thumbSize;
          y = (thumbSize - img.height) / 2;
        }
        const ct = canvas.getContext("2d") as CanvasRenderingContext2D;
        if (isLight()) {
          ct.fillStyle = "white";
          ct.fillRect(0, 0, thumbSize, thumbSize);
        }
        ct.drawImage(img, x, y, img.width, img.height);
        const b64thumb = canvas.toDataURL("image/jpeg");
        fileItem.thumbUrl = b64thumb;
        fileItem.preview = b64thumb;
        fileListSet([...fileList]);
        document.body.removeChild(img);
      };
      img.src = b64;
    };
    reader.readAsDataURL(file);
  };

  const uploadVideo = (file: RcFile) => {
    console.log("uploadVideo 尚未实现-_-!", file);
  };

  const adjustImage = (fileItem: UploadFile) => {
    /**
     * 微调原图，通过逐步降低画面品质和缩小画面尺寸来减少图片文件大小。
     * 微调过程：
     * 画面品质递降5%，直到10%为止。
     * 画面尺寸递减128px，直到画面宽度或高度小于1024px为止。
     */
    const { url, linkProps } = fileItem;
    let { b64, quality, width, height } = linkProps;
    if (b64.length <= limitSize ||
      quality <= 0.1 ||
      width <= 1024 || height <= 1024
    ) {
      // 若文件大小达标，或者已无再降级余地，则开始上传。
      fileItem.percent = 65;
      fileListSet([...fileList]);
      return;
    }

    // 对图片进行降级操作，以便达标。
    const step = 128;
    quality -= 0.05;
    quality = parseFloat(quality.toFixed(2));
    fileItem.percent = (fileItem.percent || 10) + 3;
    fileItem.linkProps.quality = quality;

    const img = document.createElement("img");
    img.style.visibility = "hidden";
    img.style.position = "absolute";
    img.style.top = "0px";
    document.body.append(img);
    img.onload = () => {
      const canvas = document.createElement("canvas");
      let x = 0, y = 0;
      if (width <= height) {
        // 竖图
        height -= step;
        img.height = height;
        canvas.width = height;
        canvas.height = height;
        x = (height - img.width) / 2;
      } else {
        // 横图、宽图
        width -= step;
        img.width = width;
        canvas.width = width;
        canvas.height = width;
        y = (width - img.height) / 2;
      }
      fileItem.linkProps.width = img.width;
      fileItem.linkProps.height = img.height;
      const ct = canvas.getContext("2d") as CanvasRenderingContext2D;
      if (isLight()) {
        ct.fillStyle = "white";
        ct.fillRect(0, 0, canvas.width, canvas.height);
      }
      ct.drawImage(img, x, y, img.width, img.height);
      b64 = canvas.toDataURL("image/jpeg", quality);
      fileItem.linkProps.b64 = b64;
      fileListSet([...fileList]);
      document.body.removeChild(img);
    };
    if (url) img.src = url;
  };

  const upload = (fileItem: UploadFile) => {
    fileItem.percent = 90;
    fileListSet([...fileList]);

    const { name, preview, linkProps } = fileItem;
    const ext = name.split(".")[1];
    const b64 = linkProps.b64.split(",")[1];
    const b64thumb = preview?.split(",")[1];
    API.bottleUpload(ext, b64, b64thumb).then((res: TYPE.Response) => {
      const filePath = res.msg;
      fileItem.name = filePath;
      fileItem.percent = 100;
      fileItem.status = "done";
      fileItem.url = apiUrl + "file/" + filePath;
      fileListSet([...fileList]);
    }).catch((res: TYPE.Response) => {
      message.error(res.msg);
      fileItem.status = "error";
      fileListSet([...fileList]);
    });
  };

  const uploadButton = (
    <Access accessible={ac.canUse("upload")}>
      <Upload
        // action={apiUrl + "api/bottle/upload"}
        accept=".jpg,.mp4"
        multiple
        maxCount={uploadMaxCount}
        listType="picture-card"
        fileList={fileList}
        beforeUpload={() => false} // 直接使用自定义的上传逻辑
        onChange={uploadOnChange}
        onPreview={(file: UploadFile) => {
          const { url, thumbUrl } = file;
          previewImageSet(url || thumbUrl || "");
          previewVisibleSet(true);
        }}
        onRemove={(file: UploadFile) => {
          const { name } = file;
          const newList = fileList.filter(item => item.name !== name);
          fileListSet(newList);
          removeFile(name);
        }}
      >
        {fileList.length < uploadMaxCount && (
          <button style={{ border: "none", backgroundColor: "unset" }}>
            <PlusOutlined />
            <div style={{ marginTop: 8 }}>选择文件</div>
          </button>
        )}
      </Upload>
    </Access>
  );

  const form = (
    <Form
      form={formRef}
      labelCol={{ flex: "90px" }}
      initialValues={{ price: 5, useFor: 0 }}
      onValuesChange={changedValues =>
        formValueSet({ ...formValues, ...changedValues })
      }
    >
      <Row gutter={{ md: 24, sm: 8 }}>
        <Col md={12} sm={24}>
          <Form.Item name="name" label="名称" rules={[{ required }]}>
            <Input maxLength={255} />
          </Form.Item>
        </Col>
        <Col md={12} sm={24}>
          <Form.Item name="useFor" label="适用于,用途" rules={[{ required }]}>
            <Select allowClear options={useForData} />
          </Form.Item>
        </Col>

        <Col md={12} sm={24}>
          <Form.Item name="price" label="单价" rules={[{ required }]}>
            <Input type="number" min="1" max="100" suffix="元" />
          </Form.Item>
        </Col>
        <Col md={12} sm={24}>
          <Form.Item name="tag" label="标签">
            <Input maxLength={255} placeholder="多个以空格分隔" />
          </Form.Item>
        </Col>

        <Col md={12} sm={24}>
          <Form.Item name="color" label="颜色" rules={[{ required }]}>
            <Input maxLength={10} />
          </Form.Item>
        </Col>
        <Col md={12} sm={24}>
          <Form.Item name="brand" label="品牌" rules={[{ required }]}>
            <Input maxLength={20} />
          </Form.Item>
        </Col>

        <Col md={12} sm={24}>
          <Form.Item name="height" label="高度" rules={[{ required }]}>
            <Input type="number" min="0" max="500" step="10" suffix="毫米" />
          </Form.Item>
        </Col>
        <Col md={12} sm={24}>
          <Form.Item name="width" label="直径" rules={[{ required }]}>
            <Input type="number" min="0" max="500" step="10" suffix="毫米" />
          </Form.Item>
        </Col>

        <Col md={12} sm={24}>
          <Form.Item name="sequ" label="显示顺序">
            <Input type="number" min="0" max="1000" />
          </Form.Item>
        </Col>
        <Col md={12} sm={24}>
          <Form.Item name="remark" label="备注">
            <Input.TextArea maxLength={255} showCount rows={1} />
          </Form.Item>
        </Col>

        <Col span={24}>
          <Form.Item name="images" label="图集" rules={[{ required }]} help={help}>
            {uploadButton}
          </Form.Item>
        </Col>
      </Row>
    </Form>
  );

  const desc = <Descriptions bordered column={2} items={descItems} />;

  const contentMap = { form, desc };

  const title = (display === "desc" ? "查看" : titleMap[action]) + "瓶子信息";

  return (
    <>
      <ProTable
        headerTitle={(
          <Access accessible={ac.canUse("add")} fallback="瓶子管理">
            <Button
              type="primary"
              icon={<PlusOutlined />}
              onClick={() => {
                formRef.resetFields();
                actionSet("Add");
                editIdSet(0);
                fileListSet([]);
                openDrawer("form");
              }}
            >
              新增瓶子
            </Button>
          </Access>
        )}
        rowKey="id"
        columns={columns}
        request={API.bottle}
        actionRef={tableRef}
      />

      <Drawer
        title={title}
        width={768}
        open={visible}
        onClose={() => visibleSet(false)}
        styles={{ body: { paddingBottom: 0 }, footer: { textAlign: "right" } }}
        footer={display === "form" ? (
          <Space>
            <Button type="primary" loading={saveLoading} onClick={save}>
              保存
            </Button>
            <Button onClick={() => visibleSet(false)}>取消</Button>
          </Space>
        ) : null}
      >
        {contentMap[display]}
      </Drawer>

      {previewImage && (
        <Image
          src={previewImage}
          wrapperStyle={{ display: "none" }}
          preview={{
            visible: previewVisible,
            onVisibleChange: visible => previewVisibleSet(visible),
            afterOpenChange: visible => !visible && previewImageSet(""),
          }}
        />
      )}
    </>
  );
};
